﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Browser;
using System.Windows.Controls;
using System.Windows.Navigation;
using WinnerCMS.BLL.GatherEngine;

namespace WinnerCMS.Silverlight.GatherUI
{
    public partial class Main : Page
    {
        private static ManualResetEvent _synchro;
        private static int _threadNum;
        private static CategoryParameter _cp;
        private static bool _allStop;
        private static bool _stoplog;
        private static bool? _logErr = false;
        private static bool? _logRight = false;
        private static bool? _logAlert = false;
        private static int _gatheringNum;
        private static int _ignoreNum;
        private static readonly object Objlock = new object();
        private string _absolutePath;
        private Thread _gatherCategoryPageThread;

        public Main()
        {
            InitializeComponent();
        }

        public IDictionary<string, string> DicParams { get; set; }
        private bool IsTest { get; set; }
        private bool IsUpdate { get; set; }
        private bool IsLeech { get; set; }
        private bool IsReverse { get; set; }

        private void Page_Loaded(object sender, RoutedEventArgs e)
        {
            if (DicParams.Count == 0)
            {
                return;
            }
            if (
                Application.Current.Host.Source.AbsoluteUri.IndexOf("/ClientBin",
                    StringComparison.CurrentCultureIgnoreCase) < 0)
            {
                this._absolutePath = "http://localhost";
            }
            else
            {
                this._absolutePath = Application.Current.Host.Source.AbsoluteUri.Substring(0,
                    Application.Current.Host.Source.AbsoluteUri.IndexOf("/ClientBin", StringComparison.Ordinal));
            }
            _threadNum = byte.Parse(this.tb_ThreadNum.Text);

            IsTest = (bool) this.cb_IsTest.IsChecked;
            IsUpdate = (bool) this.cb_IsUpdate.IsChecked;
            IsLeech = (bool) this.cb_IsLeech.IsChecked;
            IsReverse = (bool) this.cb_IsReverse.IsChecked;
            _logErr = this.cb_LogErr.IsChecked;
            _logRight = this.cb_LogRight.IsChecked;
            _logAlert = this.cb_LogAlert.IsChecked;
        }

        // 当用户导航到此页面时执行。
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
        }

        private void bt_start_Click(object sender, RoutedEventArgs e)
        {
            this.bt_start.IsEnabled = false;
            this.bt_stop.IsEnabled = true;
            this.tb_BadLinkNum.IsEnabled = false;
            this.listBox1.Items.Clear();
            _allStop = false;
            this.listBox1.Items.Add("开始采集。");
            this.tb_ProgressNum.Text = "0 / 0";
            _gatheringNum = 0;
            _ignoreNum = 0;


            _cp = new CategoryParameter(DicParams["IDs"], byte.Parse(DicParams["ModelId"]), IsReverse)
            {
                UserId = int.Parse(DicParams["UserId"]),
                NextCategoryUrl = ""
            };

            this._gatherCategoryPageThread = new Thread(() => GatherCategoryPage(_cp));
            this._gatherCategoryPageThread.Start();
        }

        private void GatherCategoryPage(object obj)
        {
            var p = obj as CategoryParameter;
            var endpoint = new Uri(this._absolutePath + "/api/Gather/GatherCategoryPage");

            var data = string.Format("IDs={0}&IsReverse={1}&NextCategoryUrl={2}&StartUrlIndex={3}&ModelId={4}", p.IDs,
                p.IsReverse, p.NextCategoryUrl, p.StartUrlIndex, p.ModelId);

            var client = new WebClient();
            client.Headers[HttpRequestHeader.Accept] = "application/json, text/javascript, */*; q=0.01";
            client.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
            client.UploadStringCompleted += CategoryPage_UploadStringCompleted;
            client.UploadStringAsync(endpoint, "POST", data);
        }

        private void CategoryPage_UploadStringCompleted(object sender, UploadStringCompletedEventArgs e)
        {
            if (_allStop)
            {
                return;
            }
            var cgr = SerializableHelper.JsonDeSerialize<CategoryGatherResult>(e.Result);

            if (string.IsNullOrEmpty(cgr.Err) == false && string.IsNullOrEmpty(cgr.NextCategoryUrl))
            {
                Writelog(cgr.GatheringUrl, "采集任务中止。" + cgr.Err, ContentPageGatherResultType.Messages);
                Stop();
                return;
            }
            if (string.IsNullOrEmpty(cgr.Err) == false)
            {
                Writelog(cgr.GatheringUrl, "采集出现错误。" + cgr.Err, ContentPageGatherResultType.Messages);
            }
            else
            {
                Writelog(cgr.GatheringUrl, "正在采集" + cgr.Urls.Count() + "页。\r\n下一栏目页是：" + cgr.NextCategoryUrl,
                    ContentPageGatherResultType.Messages);
            }

            var ths = new Thread[cgr.Urls.Count()];

            #region 线程列队

            for (var i = 0; i < ths.Length; i++)
            {
                var wp = new WebParameter
                {
                    AdminName = DicParams["AdminName"],
                    BadLinkNum = 0,
                    IsTest = IsTest,
                    ModelId = byte.Parse(DicParams["ModelId"]),
                    GatherId = cgr.GatherId,
                    IsUpdate = IsUpdate,
                    IsLeech = IsLeech,
                    UserId = int.Parse(DicParams["UserId"])
                };
                ths[i] = new Thread(GatherWeb);
                wp.Url = cgr.Urls[i];
                if (_threadNum > 0)
                {
                    _threadNum--;

                    ths[i].Start(wp);
                }
                else
                {
                    _synchro = new ManualResetEvent(false);
                    _synchro.WaitOne(); //等待   
                    _threadNum--;

                    ths[i].Start(wp);
                }
            }

            #endregion

            if (cgr.NextCategoryUrl.Length > 0)
            {
                if (_allStop)
                {
                    return;
                }
                _cp.StartUrlIndex = cgr.StartUrlIndex;
                _cp.NextCategoryUrl = cgr.NextCategoryUrl;
                GatherCategoryPage(_cp);
            }
            else
            {
                Stop();
            }
        }

        private void GatherWeb(object p)
        {
            if (_allStop)
            {
                return;
            }
            var wp = p as WebParameter;

            var endpoint = new Uri(String.Format(this._absolutePath + "/api/Gather/GatherWeb"));
            var data =
                string.Format(
                    "GatherID={0}&ModelId={1}&Url={2}&BadLinkNum={3}&IsTest={4}&IsUpdate={5}&IsLeech={6}&AdminName={7}&UserId={8}",
                    wp.GatherId, wp.ModelId, HttpUtility.UrlEncode(wp.Url), wp.BadLinkNum, wp.IsTest, wp.IsUpdate,
                    wp.IsLeech, wp.AdminName, wp.UserId);

            var client = new WebClient();
            client.Headers[HttpRequestHeader.Accept] = "application/json, text/javascript, */*; q=0.01";
            client.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
            client.UploadStringCompleted += (s, e) =>
            {
                if (e.Error != null)
                {
                    Writelog(wp.Url, e.Error.Message, ContentPageGatherResultType.Err);
                    return;
                }
                try
                {
                    var cpgr = SerializableHelper.JsonDeSerialize<ContentPageGatherResult[]>(e.Result).FirstOrDefault();

                    if (cpgr.Gathered)
                    {
                        lock (Objlock)
                        {
                            _gatheringNum++;
                        }
                    }
                    else
                    {
                        lock (Objlock)
                        {
                            _ignoreNum++;
                        }
                    }
                    Dispatcher.BeginInvoke(
                        () => { this.tb_ProgressNum.Text = string.Format("{0} / {1}", _gatheringNum, _ignoreNum); });

                    Writelog(wp.Url, cpgr.Messages, cpgr.Type);

                    lock (Objlock)
                    {
                        _threadNum++;
                    }

                    if (_threadNum > 0)
                    {
                        if (_synchro != null)
                            _synchro.Set(); //激活等待
                    }
                }
                catch (Exception ex)
                {
                    Writelog(wp.Url, ex.Message, ContentPageGatherResultType.Err);
                }
            };
            client.UploadStringAsync(endpoint, "POST", data);
        }

        #region 写日志

        /// <summary>
        ///     写日志
        /// </summary>
        /// <param name="url"></param>
        /// <param name="msg"></param>
        /// <param name="type"></param>
        private void Writelog(string url, string msg, int type)
        {
            if (_stoplog && type != ContentPageGatherResultType.Messages)
            {
                return;
            }
            if (_logRight == false && type == ContentPageGatherResultType.Right)
            {
                return;
            }
            if (_logAlert == false && type == ContentPageGatherResultType.Alert)
            {
                return;
            }
            if (_logErr == false && type == ContentPageGatherResultType.Err)
            {
                return;
            }

            var log = new StringBuilder(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:ffff\t"));
            if (string.IsNullOrEmpty(url) == false)
            {
                log.Append(url);
            }
            switch (type)
            {
                case ContentPageGatherResultType.Messages:
                    log.Append("\n消息：");
                    break;
                case ContentPageGatherResultType.Right:
                    log.Append("\n消息：");
                    break;
                case ContentPageGatherResultType.Alert:
                    log.Append("\n警告：");
                    break;
                case ContentPageGatherResultType.Err:
                    log.Append("\n错误：");
                    break;
            }
            log.Append(msg);

            Dispatcher.BeginInvoke(() => HtmlPage.Window.Invoke("Message", url, msg, type));
        }

        #endregion

        private void bt_stop_Click(object sender, RoutedEventArgs e)
        {
            this.bt_start.IsEnabled = true;
            this.bt_stop.IsEnabled = false;
            this.tb_BadLinkNum.IsEnabled = true;
            _allStop = true;
            Writelog(string.Empty, "用户停止采集。", ContentPageGatherResultType.Messages);

            if (this._gatherCategoryPageThread != null &&
                this._gatherCategoryPageThread.ThreadState == ThreadState.Running)
                this._gatherCategoryPageThread.Abort();
        }

        private void Stop()
        {
            Dispatcher.BeginInvoke(() =>
            {
                this.bt_start.IsEnabled = true;
                this.bt_stop.IsEnabled = false;
                this.tb_BadLinkNum.IsEnabled = true;
                _allStop = true;
                Writelog(string.Empty, "采集完成。", ContentPageGatherResultType.Messages);
            });
            if (this._gatherCategoryPageThread != null &&
                this._gatherCategoryPageThread.ThreadState == ThreadState.Running)
                this._gatherCategoryPageThread.Abort();
        }

        private void bt_stoplog_Click(object sender, RoutedEventArgs e)
        {
            _stoplog = !_stoplog;
            this.bt_stoplog.Content = (_stoplog == false ? "停止日志" : "开始日志");
        }

        private void LogCheckBox_Checked(object sender, RoutedEventArgs e)
        {
            _logErr = this.cb_LogErr.IsChecked;
            _logRight = this.cb_LogRight.IsChecked;
            _logAlert = this.cb_LogAlert.IsChecked;
        }

        private void bt_clearlog_Click(object sender, RoutedEventArgs e)
        {
            this.listBox1.Items.Clear();
            HtmlPage.Window.Invoke("ClearLog");
        }

        private void tb_ThreadNum_TextChanged(object sender, TextChangedEventArgs e)
        {
            int.TryParse(this.tb_ThreadNum.Text, out _threadNum);
        }

        private void cb_IsTest_Checked(object sender, RoutedEventArgs e)
        {
            IsTest = (bool) this.cb_IsTest.IsChecked;
        }

        private void cb_IsReverse_Checked(object sender, RoutedEventArgs e)
        {
            IsReverse = (bool) this.cb_IsReverse.IsChecked;
        }

        private void cb_IsLeech_Checked(object sender, RoutedEventArgs e)
        {
            IsLeech = (bool) this.cb_IsLeech.IsChecked;
        }

        private void cb_IsUpdate_Checked(object sender, RoutedEventArgs e)
        {
            IsUpdate = this.cb_IsUpdate.IsChecked.Value;
        }
    }
}